﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data.Services.Client;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using System.Xml.Linq;
using Caliburn.Micro;
using OrbitOne.CRMLyncInfoDashboard.CRM;
using OrbitOne.CRMLyncInfoDashboard.CRM.OData;
using OrbitOne.CRMLyncInfoDashboard.CRM.Organization;
using OrbitOne.CRMLyncInfoDashboard.CRM.OrganizationData;
using OrbitOne.CRMLyncInfoDashboard.CRM.WCF;
using OrbitOne.CRMLyncInfoDashboard.Lync;

namespace OrbitOne.CRMLyncInfoDashboard.Shell
{
    [Export(typeof(IScreen))]
    public partial class SummaryViewModel : Screen, ISummaryViewModel, IDataCollection
    {
        private readonly ILyncConversationProvider m_lyncConversationProvider;
        private readonly ICrmDataProviderFactory m_crmDataProviderFactory;
        private readonly IWindowUtility m_windowUtility;
        private readonly ILyncDashboardSavedQueryProvider m_lyncDashboardSavedQueryProvider;
        private Endpoint[] m_endpoints;

        public IShellViewModel Conductor { get { return Parent as IShellViewModel; } }

        private bool m_isBusy;



        public bool IsBusy
        {
            get { return m_isBusy; }
            set
            {
                m_isBusy = value;
                base.NotifyOfPropertyChange(() => IsBusy);
            }
        }

        public List<AccordionItemModel> EntityCategories
        {
            get { return m_entityCategories; }
            private set
            {
                m_entityCategories = value;
                try
                {
                    base.NotifyOfPropertyChange(() => EntityCategories);
                }
                catch (System.Exception)
                {

                }
            }
        }

        public Endpoint[] Messages
        {
            get { return m_messages; }
            set
            {
                m_messages = value;

                base.NotifyOfPropertyChange(() => Messages);
            }
        }

        public EntityData ContactData
        {
            get { return m_contacts; }
            set
            {

                m_contacts = value;
                UpdateAccordionItemModels(EntityModelTypes.ContactModel, m_contacts);
                m_contactsLoaded = true;
                CheckIfOnlyOneMatch();
            }
        }

        private void UpdateAccordionItemModels(EntityModelTypes entityModelType, EntityData model)
        {
            if (model.Data.Count() <= 0)
            {
                return;
            }

            m_lyncDashboardSavedQueryProvider.GetByName(entityModelType, OnConfigurationLoaded, model);
        }

        public EntityData LeadData
        {
            get { return m_leads; }
            set
            {
                m_leads = value;
                UpdateAccordionItemModels(EntityModelTypes.LeadModel, m_leads);
                m_leadsLoaded = true;
                CheckIfOnlyOneMatch();
            }
        }

        public EntityData AccountData
        {
            get { return m_accounts; }
            set
            {
                m_accounts = value;
                UpdateAccordionItemModels(EntityModelTypes.AccountModel, m_accounts);
                m_accountsLoaded = true;
                CheckIfOnlyOneMatch();
            }
        }

        private void CheckIfOnlyOneMatch()
        {
            IsBusy = true;
            if (!m_accountsLoaded || !m_contactsLoaded || !m_leadsLoaded)
            {
                IsBusy = false;
                return;
            }

            var entities = new[]
                               {
                                   AccountData.Data,
                                   ContactData.Data,
                                   LeadData.Data
                               };
            if (entities.SelectMany(e => e).ToList().Count() != 1)
            {
                IsBusy = false;
                return;
            }

            var entityModel = entities.Single(e => e.Count() == 1).Single();

            IsBusy = false;
            EntitySelected(entityModel);
        }


        [ImportingConstructor]
        public SummaryViewModel(ILyncConversationProvider lyncConversationProvider,
            ICrmDataProviderFactory crmDataProviderFactory,
            IWindowUtility windowUtility,
            ILyncDashboardSavedQueryProvider lyncDashboardSavedQueryProvider)
        {
            m_lyncConversationProvider = lyncConversationProvider;
            m_crmDataProviderFactory = crmDataProviderFactory;
            m_windowUtility = windowUtility;
            m_lyncDashboardSavedQueryProvider = lyncDashboardSavedQueryProvider;

        }


        protected override void OnInitialize()
        {
            base.OnInitialize();
            base.OnActivate();

            RefreshContents();
        }

        public void Log(params Endpoint[] msg)
        {
            Messages = msg;
        }

        public void EntitySelected(IEntityModel entity)
        {
            Conductor.ShowDetailView(entity, m_endpoints);
        }

        public void NewAccount()
        {
            var phoneNumber = m_endpoints.FirstOrDefault(e => e.Type == EndpointType.Telephone);
            var email = m_endpoints.FirstOrDefault(e => e.Type == EndpointType.Email);

            var accountModel = new AccountModel
                                   {
                                       Telephone1 = phoneNumber != null ? phoneNumber.Uri : "",
                                       EMailAddress1 = email != null ? email.Uri : ""
                                   };
            m_windowUtility.CreateNewEntityWindow(accountModel, new Expression<Func<AccountModel, object>>[] {am => am.Telephone1});
        }

        public void NewContact()
        {
           var phoneNumber = m_endpoints.FirstOrDefault(e => e.Type == EndpointType.Telephone);
            var email = m_endpoints.FirstOrDefault(e => e.Type == EndpointType.Email);

            var contactModel = new ContactModel
                                   {
                                       Telephone1 = phoneNumber != null ? phoneNumber.Uri : "",
                                       EMailAddress1 = email != null ? email.Uri : ""
                                   };
            
            m_windowUtility.CreateNewEntityWindow(contactModel, new Expression<Func<ContactModel, object>>[]{cm => cm.Telephone1});
        }

        public void NewLead()
        {
           var phoneNumber = m_endpoints.FirstOrDefault(e => e.Type == EndpointType.Telephone);
            var email = m_endpoints.FirstOrDefault(e => e.Type == EndpointType.Email);

            var leadModel = new LeadModel
                                   {
                                       Telephone1 = phoneNumber != null ? phoneNumber.Uri : "",
                                       EMailAddress1 = email != null ? email.Uri : ""
                                   };
            m_windowUtility.CreateNewEntityWindow(leadModel, new Expression<Func<LeadModel, object>>[]{lm => lm.Telephone1});
        }

        public EntityMetadata[] EntityMetadata
        {
            get { return m_entityMetadata; }
            set
            {
                m_entityMetadata = value;
                base.NotifyOfPropertyChange(() => EntityMetadata);
            }
        }

        public string AccountDisplayNamePlural
        {
            get { return m_accountDisplayNamePlural; }
            set { m_accountDisplayNamePlural = value; }
        }

        public string ContactDisplayNamePlural
        {
            get { return m_contactDisplayNamePlural; }
            set { m_contactDisplayNamePlural = value; }
        }

        public string LeadDisplayNamePlural
        {
            get { return m_leadDisplayNamePlural; }
            set { m_leadDisplayNamePlural = value; }
        }

        public void RefreshContents()
        {
            EntityCategories = new List<AccordionItemModel>();
            m_accountsLoaded = m_contactsLoaded = m_leadsLoaded = false;

            m_endpoints = m_lyncConversationProvider.GetEndpoints();

            Log(m_endpoints);

            m_crmDataProviderFactory.CrmDataProviders
                .ToList()
                .ForEach(dp => dp.GetMatchingEntities(this, m_endpoints));
        }

        private void OnConfigurationLoaded(IAsyncResult ar)
        {
            var userState = (ar.AsyncState as UserState);
            if (userState == null) return;

            var query = (DataServiceQuery<SavedQuery>)userState.Query;
            if (query == null) return;

            var model = userState.EntityData;

            var endExecute = query.EndExecute(ar);
            var dataServiceCollection = new DataServiceCollection<SavedQuery>(endExecute);
            var entities = dataServiceCollection.ToList();

            var lyncDashboardSavedQueryModel = GetEntityModels<SavedQuery, SavedQueryModel>(entities).FirstOrDefault();
            if (lyncDashboardSavedQueryModel == null) return;

            DisplayGridData(lyncDashboardSavedQueryModel.LayoutXml, model);
        }

        private void DisplayGridData(XElement element, EntityData model)
        {
            var cells = element.Descendants("cell").ToList();
            model.SavedQueryColumnInfos = cells.Select(cell =>
                                                           {
                                                               var nameAttribute = cell.Attribute("name");
                                                               if (nameAttribute == null)
                                                               {
                                                                   throw new ArgumentNullException("The name attribute is missing from the SavedQuery cell definition.");
                                                               }

                                                               var widthAttribute = cell.Attribute("width");
                                                               if (widthAttribute == null)
                                                               {
                                                                   throw new ArgumentNullException("The width attribute is missing from the SavedQuery cell definition.");
                                                               }
                                                               return new SavedQueryColumnInfo(nameAttribute.Value,
                                                                                                int.Parse(widthAttribute.Value));
                                                           }).ToList();


            var savedQueryDisplayItems = model.Data
                .Select(entityModel =>
                            {
                                var type = model.Data.First().GetType();
                                var propertyInfos = model.SavedQueryColumnInfos
                                    .Select(ci => new
                                    {
                                        Property = type.GetProperties().Where(p => p.Name.Equals(ci.FieldName, StringComparison.InvariantCultureIgnoreCase)).First(),
                                        Width = ci.Width
                                    }).ToList();

                                var fieldValues = propertyInfos.Select(pi => new DisplayColumn(pi.Width, (pi.Property.GetValue(entityModel, null) ?? string.Empty).ToString()))
                                    .ToList();

                                return new SavedQueryDisplayItem(fieldValues, entityModel);

                            }).ToList();

            var gridHeaderLabels = model.SavedQueryColumnInfos.Select(ci => new GridHeaderLabel(ci.Width, model.EntitytMetadataModel[ci.FieldName]))
                .ToList();

            EntityCategories = EntityCategories.Concat(new List<AccordionItemModel>
                                                           {
                                                               new AccordionItemModel(new AccordionItemHeaderModel(model.EntitytMetadataModel.DisplayCollectionName, model.Data.Count()),
                                                                   model.Data,
                                                                   gridHeaderLabels,
                                                                   savedQueryDisplayItems,
                                                                   model.SequenceNumber)
                                                           })
                                                           .OrderBy(aim => aim.SequenceNumber)
                                                           .ToList();
        }

        private static TModel[] GetEntityModels<T, TModel>(IEnumerable<T> entities)
            where TModel : class
            where T : class
        {
            return entities.Select(e => e == null ? null : (TModel)Activator.CreateInstance(typeof(TModel), e)).ToArray();
        }
    }


}